# region
import os
import re
import abc
import logging
import numpy as np
from typing import List

from abc import ABC, abstractmethod
from pathlib import Path
from spire.doc import *
from spire.doc.common import *

logging.basicConfig(
    level=logging.DEBUG,
    format='%(levelname)8s: %(message)s'
)
# endregion

# region
import pandas as pd
from pandas import Series, DataFrame


# endregion


class Basic(ABC):

    # def __init__(self):
    # self.stu_list: DataFrame = pd.DataFrame(columns=['学号', '姓名'])
    # self.filenames: Series = pd.Series()

    @staticmethod
    def get_filenames_in_folder(folder_path: str, file_types: []):
        try:
            # 验证文件夹是否存在
            if not os.path.isdir(folder_path):
                logging.error(f"文件夹不存在 - {folder_path}")
                return pd.Series([], name='文件名')

            folder = Path(folder_path)
            matched_files = []
            file_types = [ext.lower() for ext in file_types]  # 统一小写处理

            # 收集所有匹配的文件
            for item in folder.iterdir():
                if item.is_file() and item.suffix.lower() in file_types:
                    # 根据返回类型处理路径
                    matched_files.append(item.name)

            # 按文件名排序
            matched_files.sort()

            logging.debug(f"在 {folder_path} 中找到 {len(matched_files)} 个 {file_types} 文件")
            filenames = pd.Series(matched_files, name='文件名')
            return filenames

        except Exception as e:
            logging.error(f"获取文件列表时出错: {str(e)}")
            return pd.Series([], name='文件名')

    @staticmethod
    def get_stu_list_from(xlsx_file_pathname: str) -> DataFrame:
        try:
            logging.info('读取学生名单')
            # 读取Excel文件中的指定列
            stu_list = pd.read_excel(
                xlsx_file_pathname,
                usecols=['学号', '姓名', '班级'],  # type: ignore
                dtype={'学号': int, '姓名': str, '班级': str},
                header=0,
            )
            logging.info('✅ 读取学生名单成功')

            return stu_list
        except Exception as e:
            logging.error(f"❌ 读取学生名单失败: {str(e)}")
            exit(0)

    @staticmethod
    def extract_parts_by_pattern(cls, se: Series, pattern: str, part_index=1):
        logging.debug("int __extract_parts_by_pattern()")

        if pattern is None or pattern == "":
            logging.error("参数 pattern 不能为空")
            exit(0)

        # 提取函数
        def extract_part(s):
            match = re.compile(pattern).match(s)
            if match:
                return match.group(part_index)
            return None  # 不符合格式的返回None

        # 应用提取函数并返回Series
        return se.apply(extract_part)

    @staticmethod
    def fix_时长(cls, se_时长, loop=3) -> Series:
        """
        个别同学的视频时长可能会非常长，从而“衬托”其他同学的时长很短。
        """
        for _ in range(loop):
            # 1. 找到最大值（极大值）
            max_val = se_时长.max()

            # 2. 计算第二大值（去重后排序，取倒数第二个值）
            # 去重并排序（升序）
            unique_sorted = se_时长.sort_values()
            # 第二大值是排序后的倒数第二个值
            second_max = unique_sorted.iloc[-2]
            # 3. 将所有极大值替换为第二大值
            s_replaced = se_时长.replace(max_val, second_max)

        return s_replaced

    @staticmethod
    def fix_页数(cls, se_页数: Series, loop=3):
        """
        个别同学的文档可能会非常长，从而导致分数反而非常低。
        """
        for _ in range(loop):
            # 1. 找到最大值（极大值）
            max_val = se_页数.max()

            # 2. 计算第二大值
            unique_sorted = se_页数.sort_values()
            second_max = unique_sorted.iloc[-2]

            # 3. 用第二大值替换极大值
            s_replaced = se_页数.replace(max_val, second_max)

        return s_replaced

    @staticmethod
    def check_filenames_format(filenames: Series, *patterns):
        logging.debug("检查文件名是否符合规则")
        invalid_strings = []
        for s in filenames:
            if ' ' in s:
                invalid_strings.append(s)
                continue
            match = match_any_pattern(s, patterns)
            if match is False:
                invalid_strings.append(s)

        if invalid_strings:
            logging.debug("以下字符串不符合格式要求:")
            for s in invalid_strings:
                logging.debug(f"  - {s}")
            logging.error(f"\n❌ 总计: {len(invalid_strings)} 个字符串不符合规则")
            exit(0)
        else:
            logging.info("✅ 所有文件名都符合格式要求!")

    @staticmethod
    def check_filenames_duplicates(filenames: Series):
        logging.info('检查文件名是否有重复')
        # 去掉后缀再判断重复
        new_se = filenames.str.rsplit('.', n=2).str[0]

        # 计算每个值的出现次数
        value_counts = new_se.value_counts()

        # 找出重复项（出现次数大于1）
        duplicates = value_counts[value_counts > 1]

        if not duplicates.empty:
            logging.info(f"列表中有 {len(duplicates)} 个重复项:")
            for i, (value, count) in enumerate(duplicates.items(), 1):
                logging.info(f"{i}. {value} (出现次数: {count})")
            logging.error('文件名有重复')
            exit(0)
        else:
            logging.info('✅ 文件名无重复')

    @staticmethod
    def extract_parts_from_filename(filenames: Series, pattern: str, part_index=1) -> Series:
        if pattern is None or pattern == "":
            logging.error("参数 pattern 不能为空")
            exit(0)

        # 提取函数
        def extract_part(s):
            match = re.compile(pattern).match(s)
            if match:
                return match.group(part_index)
            return None  # 不符合格式的返回None

            # 应用提取函数并返回Series

        return filenames.apply(extract_part)

    @staticmethod
    def get_文档_页数(base_folder: str, filenames: Series):
        # 1. 拼接完整文件路径（文件夹路径 + 文件名）
        se_full_paths = filenames.apply(lambda name: os.path.join(base_folder, name))

        # 2. 遍历每个路径，调用函数获取页数，生成新的Series
        se_page_counts = se_full_paths.apply(lambda path: get_word_pages(path))

        return se_page_counts

    @staticmethod
    def merge_dataframes(df_list: List[pd.DataFrame], how: str = 'outer') -> pd.DataFrame:
        """
        将多个含“学号”列的DataFrame合并为一个

        参数:
            df_list: 包含多个DataFrame的列表，每个DataFrame有两列，其中一列名为“学号”
            how: 合并方式，'outer'保留所有学号（默认），'inner'只保留共同学号

        返回:
            合并后的DataFrame
        """
        if not df_list:
            raise ValueError("输入的DataFrame列表不能为空")

        # 初始化合并结果为第一个DataFrame。第一个DataFrame应该是学生列表。
        merged_df = df_list[0].copy()

        # 遍历剩余DataFrame，依次合并
        for df in df_list[1:]:
            # 检查是否包含“学号”列
            if '学号' not in df.columns:
                raise ValueError("所有DataFrame必须包含'学号'列")

            # 检查是否只有两列
            if len(df.columns) != 2:
                raise ValueError("所有DataFrame必须仅包含两列")

            # 获取当前DataFrame的非学号列名（作为合并后的新列）
            other_col = [col for col in df.columns if col != '学号'][0]

            # 合并：以“学号”为键，保留新列
            merged_df = merged_df.merge(
                df[['学号', other_col]],  # 只取需要的两列，避免潜在重复
                on='学号',
                how=how
            )

        return merged_df


def match_any_pattern(text, patterns):
    for pattern in patterns:
        if bool(re.match(pattern, text)):
            return True  # 找到第一个匹配就返回
    return False  # 所有模式都不匹配


def get_word_pages(docx_path: str) -> int:
    # 创建Document对象并加载Word文档
    doc = Document()
    doc.LoadFromFile(docx_path)

    # 通过内置文档属性获取页数
    return doc.BuiltinDocumentProperties.PageCount


if __name__ == "__main__":
    ret = match_any_pattern('hello.mp4', '^[a-z]*.txt$', '^[a-z]*.pdf$')
    print(ret)
